package com.leyou.auth.service;

import com.leyou.UserClient;
import com.leyou.auth.config.JwtProperties;
import com.leyou.auth.entity.AppInfo;
import com.leyou.auth.entity.ApplicationInfo;
import com.leyou.auth.mapper.ApplicationInfoMapper;
import com.leyou.common.auth.entity.Payload;
import com.leyou.common.auth.entity.UserInfo;
import com.leyou.common.auth.utils.JwtUtils;
import com.leyou.common.auth.utils.RsaUtils;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.CookieUtils;
import com.leyou.user.dto.UserDTO;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Appinfo;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @package com.leyou.auth.service
 * @description:
 * @author: 86157
 * @date 2019/7/7 18:38
 * @version: V1.0
 */
@Slf4j
@Service
public class AuthService {

    @Autowired
    private JwtProperties prop;

    @Autowired
    private UserClient userClient;

    @Autowired
    private StringRedisTemplate redisTemplate;


    private static final String USER_ROLE = "role_user";

    /**
     * 用户登录
     *
     * @param username
     * @param password
     * @param response
     */
    public void login(String username, String password, HttpServletResponse response) {

        try {
            UserDTO user = userClient.queryUserByUsernameAndPassword(username, password);
            // 生成userInfo, 没写权限功能，暂时都用guest
            UserInfo userInfo = new UserInfo(user.getId(), user.getUsername(), USER_ROLE);
            //生成token
            String token = JwtUtils.generateTokenExpireInMinutes(userInfo,
                    prop.getPrivateKey(), prop.getUser().getExpire());

            //写入cookie
            CookieUtils.newCookieBuilder().response(response).domain(prop.getUser().getCookieDomain()).name(prop.getUser().getCookieName())
                    .value(token).httpOnly(true).build();
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
    }


    /**
     * 验证用户信息
     *
     * @param request
     * @param response
     * @return
     */
    public UserInfo verifyUser(HttpServletRequest request, HttpServletResponse response) {

        try {
            //读取cookie，获取token
            String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
            //获取token
            Payload<UserInfo> payLoad = JwtUtils.getInfoFromToken(token,prop.getPublicKey(), UserInfo.class);


            //获取token的id,校验黑名单
            String id = payLoad.getId();
            Boolean key = redisTemplate.hasKey(id);
            if (key != null && key) {
                throw new LyException(ExceptionEnum.UNAUTHORIZED);
            }

            //获取过期日期
            Date expiration = payLoad.getExpiration();
            //获取过期剩余时间
            Long remainTime = expiration.getTime() - System.currentTimeMillis();
            // 判断token过期时间距离现在是否小于10分钟
            if (remainTime < prop.getUser().getMaxRemainTime()) {
                //过了刷新时间，则重新生成token
                String newToken = JwtUtils.generateTokenExpireInMinutes(payLoad.getUserInfo(), prop.getPrivateKey(), prop.getUser().getExpire());
                //写入cookie
                //写入cookie
                CookieUtils.newCookieBuilder().response(response).domain(prop.getUser().getCookieDomain()).name(prop.getUser().getCookieName())
                        .value(token).httpOnly(true).build();
            }

            return payLoad.getUserInfo();
        } catch (Exception e) {
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }
    }

    /**
     * 退出登录
     *
     * @param request
     * @param response
     */
    public void logout(HttpServletRequest request, HttpServletResponse response) {

        //获取token
        String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
        //解析token
        Payload<Object> payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey());
        //获取id，有效期剩余时间
        String id = payload.getId();
        long time = payload.getExpiration().getTime() - System.currentTimeMillis();

        // 写入redis, 剩余时间超过5秒以上才写
        if (time > 5000) {
            redisTemplate.opsForValue().set(id, "", time, TimeUnit.MILLISECONDS);
        }
        //删除cookie
//        Cookie cookie = new Cookie(prop.getUser().getCookieName(), "");
//        cookie.setMaxAge(0);
//        cookie.setDomain(prop.getUser().getCookieDomain());
//        response.addCookie(cookie);
        CookieUtils.deleteCookie(prop.getUser().getCookieName(), prop.getUser().getCookieDomain(), response);
    }


    @Autowired
    private ApplicationInfoMapper appMapper;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    /**
     * 微服务认证并申请令牌
     *
     * @param id
     * @param secret
     * @return
     */
    public String authorization(Long id, String secret) {
//        - 验证id和secret
        ApplicationInfo appInfo = appMapper.selectByPrimaryKey(id);
        if (appInfo == null) {
            throw new LyException(ExceptionEnum.INVALID_SERVER_ID_SECRET);
        }
        if (!passwordEncoder.matches(secret, appInfo.getSecret())) {
            //密码不正确
            throw new LyException(ExceptionEnum.INVALID_SERVER_ID_SECRET);
        }

        //可以调用的服务id
        List<Long> targetId = appMapper.queryTargetIdList(id);

        //封装AppInfo
        AppInfo newApp= new AppInfo();
        newApp.setId(id);
        newApp.setServiceName(appInfo.getServiceName());
        newApp.setTargetList(targetId);

        //生成tooken并返回
        return JwtUtils.generateTokenExpireInMinutes(newApp, prop.getPrivateKey(), prop.getApp().getExpire());
    }
}